package com.atguigu.tingshu.common.aspect;

import cn.hutool.core.util.StrUtil;
import com.atguigu.tingshu.common.annotation.NeedLogin;
import com.atguigu.tingshu.common.execption.BusinessException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 登录校验切面
 * 用于校验用户是否登录，并将userId存入ThreadLocal
 */
@Aspect
@Component
@Slf4j
public class LoginAspect {

    /**
     * 定义切点：所有标注了 @NeedLogin 注解的方法
     */
    @Pointcut("@annotation(com.atguigu.tingshu.common.annotation.NeedLogin)")
    public void loginPointcut() {
    }

    /**
     * 环绕通知：在方法执行前校验用户登录状态，并将userId存入ThreadLocal
     */
    @Around("loginPointcut() && @annotation(needLogin)")
    public Object around(ProceedingJoinPoint joinPoint, NeedLogin needLogin) throws Throwable {
        try {
            // 获取当前请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes();
            if (attributes == null) {
                log.error("无法获取请求上下文");
                throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);
            }
            HttpServletRequest request = attributes.getRequest();
            // 从请求头中获取网关注入的 userId
            String userIdStr = request.getHeader("userId");
            // 校验 userId 是否为空
            if (StrUtil.isBlank(userIdStr)) {
                log.warn("用户未登录，userId 为空");
                throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);
            }
            // 解析 userId 并存入 ThreadLocal
            Long userId = Long.parseLong(userIdStr);
            AuthContextHolder.setUserId(userId);
            log.debug("用户登录校验通过，userId: {}", userId);
            // 执行目标方法
            return joinPoint.proceed();
        } finally {
            // 方法执行完毕后，清除 ThreadLocal，防止内存泄漏
            AuthContextHolder.removeUserId();
        }
    }
}
